#include "SSCompanyReportManager.h"

#include <core/system/system_util.h>
#include <core/time/time.h>

#include "CompanyReport.h"
#if WIN32
#include <windows.h>
#endif

using namespace qb::base;
class StatDumper {
  typedef qb::base::HighResTime HighResTime;
  typedef CReportManagerBase::kActionType kActionType;
  enum { kActionTypeCount = CReportManagerBase::kActionTypeCount };
  struct StatInfo {
    kActionType type;  // 当前操作的类型
    double time;       // 当前操作的起始时间
    double last;       // 当前操作的持续时间
    double gap;        // 与上次的间隔时间
  };
  typedef qb::base::BaseString BaseString;
  typedef list<StatInfo> StatInfoList;

 protected:            // 不变量
  int m_item_size;     // T的大小
  int m_max_count;     // m_datas允许的最大个数
  uint32_t m_log_gap;  // 写日志的间隔时间
 protected:
  int m_total;  // 总共个数,包括删除
  int m_length;
  uint32_t m_log_last;   // 最近入队时间
  int m_log_total;       // 写日志时的total
  StatInfoList m_datas;  // 最近加入的统计信息队列
  double m_speed;  // 调用速度,多少次每秒,m_datas中首尾时间差/个数
  HighResTime m_total_last;  // m_datas中的last之和
  HighResTime m_total_gap;   // m_datas中的gap之和
  HighResTime m_total_action;
  string m_name;
  int m_action[kActionTypeCount];
  CReportManagerBase* m_reportmanager;

 public:
  StatDumper(CReportManagerBase* rm)
      : m_reportmanager(rm),
        m_item_size(0),
        m_total(0),
        m_max_count(1024),
        m_log_last(GetTickCount()),
        m_log_gap(4000),
        m_length(0),
        m_speed(0.0),
        m_log_total(0),
        m_total_last(0.0),
        m_total_gap(0.0),
        m_total_action(0.0) {
    for (int i = 0; i < kActionTypeCount; i++) m_action[i] = 0.0;
  }
  ~StatDumper() {}
  void SetItemSize(int itemSize) { m_item_size = itemSize; }
  void SetName(const char* name) { m_name = name ? name : ""; }
  void Add(double time, kActionType type, int length) {
    m_total++;
    int ndelete = m_datas.size() - m_max_count;
    for (StatInfoList::iterator it = m_datas.begin();
         ndelete > 0 && it != m_datas.end();) {
      StatInfo& si = *it;
      m_total_gap -= si.gap;
      m_total_last -= si.last;
      it = m_datas.erase(it);
      ndelete--;
    }
    m_length = length;
    m_total_last += time;
    m_total_action += time;
    m_action[type] += 1;

    HighResTime cur;
    StatInfo si;
    si.last = time;
    si.time = cur - HighResTime(time);
    si.type = type;
    si.gap = 0.0;
    if (m_datas.size() > 0) {
      StatInfoList::reverse_iterator rit = m_datas.rbegin();
      StatInfo end = *rit;
      si.gap = si.time - (end.time + end.last);
      m_total_gap += si.gap;

      StatInfoList::iterator it = m_datas.begin();
      StatInfo& start = *it;
      m_speed =
          (si.time + si.last - start.time) * 1000000 / (m_datas.size() + 1);
    } else
      m_speed = si.last * 1000000 / 1.0;
    m_datas.push_back(si);
    uint32_t tick = GetTickCount();
    if (m_log_last + m_log_gap <= tick) {
      Dump();
      m_log_last = tick;
    }
  }
  const char* GetActionName(kActionType type) {
    const char* names[] = {"Add",       "Clear",     "ClearReport",
                           "Reserve",   "GetReport", "GetReportOrAdd",
                           "SetReport", "None"};
    int index = type;
    index = (index < 0 || index >= kActionTypeCount) ? kActionTypeCount : index;
    return names[index];
  }
  void Dump() {
    int nincrease = m_total - m_log_total;
    int ncount = m_datas.size();
    if (ncount <= 0 || m_total <= 0) return;
    uint32_t tick = GetTickCount();
    if (tick == m_log_last) return;

    int mem = m_reportmanager->GetMemory(0);
    int mem_real = m_reportmanager->GetMemory(1);
    float mem_ratio = mem == 0 ? 0 : (mem_real * 100.0 / mem);
    int nsize = m_reportmanager->GetSize();
    float speed = nincrease * 1000 / (tick - m_log_last);
    BaseString str = qb::base::os::fmtstr(
        "[MEMORY][REPORT]0x%8x-%24s,len:%6d,total:%8d,speed:%.2f/s,item:%4d "
        "mem:%8d/%8d/%2.2f%% size:%8d,last_avg:%8dus gap_avg:%8dus  ",
        this, m_name.c_str(), m_length, m_total, speed, m_item_size, mem_real,
        mem, mem_ratio, nsize, m_total_last.microsec() / ncount,
        m_total_gap.microsec() / ncount);
    for (int i = 0; i < kActionTypeCount; i++) {
      str +=
          qb::base::os::fmtstr("%s-%dms-%.2f, ", GetActionName((kActionType)i),
                               m_action[i], m_action[i] * 100.0 / m_total);
    }
    // qb::base::DebugView(0, 0, 0, "%s", str.c_str());
    m_log_total = m_total;
  }
};

CReportManagerBase::CReportManagerBase() : m_dumper(new StatDumper(this)) {}
CReportManagerBase::~CReportManagerBase() {
  if (m_dumper) delete m_dumper;
  m_dumper = nullptr;
}
int CReportManagerBase::GetSize() const { return 0; }
int CReportManagerBase::GetMemory(int mode) { return 0; }
void CReportManagerBase::SetName(const char* name) {
  if (m_dumper) m_dumper->SetName(name);
}
void CReportManagerBase::SetItemSize(int itemSize) {
  if (m_dumper) m_dumper->SetItemSize(itemSize);
}
void CReportManagerBase::DumpStatus(int mode /*= 0*/) const {
  ScopedLockType lock(const_cast<LockType&>(m_mutex));
  const_cast<StatDumper*>(m_dumper)->Dump();
}

CReportManagerBase::ScopedDumper::ScopedDumper(StatDumper* dumper,
                                               kActionType type, int length)
    : m_dumper(dumper), m_type(type), m_length(length) {}
CReportManagerBase::ScopedDumper::~ScopedDumper() {
  m_dumper->Add(m_timer.elapsed(), m_type, m_length);
}

CSSReportManager<CBondCompanyRep>* GetBrokerReportManager() {
  static CSSReportManager<CBondCompanyRep> mng;
  return &mng;
}

CSSReportManager<CBondCompanyRep>* GetBrokerMixedBPReportManager() {
  static CSSReportManager<CBondCompanyRep> mngMixed;
  return &mngMixed;
}

CSSReportManager<CBondBankRep>* GetBankClientReportManager() {
  static CSSReportManager<CBondBankRep> mng;
  return &mng;
}

CSSReportManager<CBondBankRep>* GetBankReportManager() {
  static CSSReportManager<CBondBankRep> mng;
  return &mng;
}

CSSReportManager<CXCQuoteRep>* GetXCQuoteReportManager() {
  static CSSReportManager<CXCQuoteRep> mng;
  return &mng;
}

CSSCFETSReportManager<CCFETSQuoteRep>* GetCfetsReportManager() {
  static CSSCFETSReportManager<CCFETSQuoteRep> mng;
  return &mng;
}

CSSCFETSDealQuoteManager<CCFETSDealQuoteRep>* GetCfetsDealQuoteManager() {
  static CSSCFETSDealQuoteManager<CCFETSDealQuoteRep> mng;
  return &mng;
}